/*
    The NeonX Library

    Copyright (c) 2013 darkcat

    Code covered by the MIT License
*/

#pragma once

// nx...
#include "config/general.h"
// nx_is_reference
#include "type/typedetect.h"
// type::none_t
#include "type/typetools.h"
// nx::swap
#include "algorithm/algorithm.h"
// nx::alloc
#include "memory/memalloc.h"
// nx_assert
#include "bugfix/assert.h"

//////////////////////////////////////////////////////////////////////////
NX_BEG
//////////////////////////////////////////////////////////////////////////

namespace private_
{
    class ValidBase_
    {
    protected:
        bool flag_;

    public:
        ValidBase_(bool f) : flag_(f) {}

        operator bool (void) const { return flag_; }
        bool operator!(void) const { return !flag_; }

        void reset(void)          { flag_ = false; }

        ValidBase_& swap(ValidBase_& rhs)
        {
            nx::swap(flag_, rhs.flag_);
            return (*this);
        }
    };

    template <typename T, bool Check_ = nx_is_reference(T)>
    class Valid_;

    template <typename T>
    class Valid_<T, true> : public ValidBase_
    {
    public:
        typedef typename nx_no_qualifier(
                typename nx_no_reference(T)) type_t;
        typedef typename nx_no_reference(T)* value_t;

    protected:
        value_t value_;

    public:
        Valid_(void)         : ValidBase_(false), value_(NX_NULL) {}
        Valid_(type::none_t) : ValidBase_(false), value_(NX_NULL) {}
        Valid_(T r)          : ValidBase_(false), value_(NX_NULL)
        { (*this) = r; }

        T             operator* (void)       { return *value_; }
        const type_t& operator* (void) const { return *value_; }
        value_t       operator->(void)       { return value_; }
        const type_t* operator->(void) const { return value_; }

        Valid_& operator=(T r)
        {
            value_ = &r;
            flag_  = true;
            return (*this);
        }

        void reset(void)
        {
            ValidBase_::reset();
            value_ = NX_NULL;
        }
    };

    template <typename T>
    class Valid_<T, false> : public ValidBase_
    {
    public:
        typedef typename nx_no_qualifier(T) type_t;
        typedef T* value_t;

    protected:
        value_t value_;

    public:
        Valid_(void)            : ValidBase_(false), value_(NX_NULL) {}
        Valid_(type::none_t)    : ValidBase_(false), value_(NX_NULL) {}
        Valid_(const type_t& r) : ValidBase_(false), value_(NX_NULL)
        { (*this) = r; }

        virtual ~Valid_() { reset(); }

        T&            operator* (void)       { return *value_; }
        const type_t& operator* (void) const { return *value_; }
        value_t       operator->(void)       { return value_; }
        const type_t* operator->(void) const { return value_; }

        Valid_& operator=(const type_t& r)
        {
            if (flag_) reset();
            value_ = nx::alloc<type_t>(r);
            flag_  = true;
            return (*this);
        }

        void reset(void)
        {
            ValidBase_::reset();
            nx::free(const_cast<type_t*>(value_));
            value_ = NX_NULL;
        }
    };
}

/*
    Clearly define an invalid value
*/

template <typename T>
class valid : public private_::Valid_<T>
{
    typedef private_::Valid_<T> base_t;

public:
    valid(void)           : base_t()  {}
    valid(type::none_t n) : base_t(n) {}
    valid(T v)            : base_t(v) {}

    template <typename U>
    valid(valid<U>& r)
    { (*this) = r; }

public:
    using base_t::operator*;
    using base_t::operator=;

    template <typename U>
    valid& operator=(valid<U>& r)
    {
        if (r)
        {
            (*this) = (*r);
        }
        else
        {
            base_t::reset();
        }
        return (*this);
    }

    template <typename U>
    valid& swap(valid<U>& rhs)
    {
        if (rhs && (*this))
        {
            base_t::swap(rhs);
            nx::swap(*(*this), *rhs);
        }
        else if (rhs)
        {
            (*this) = rhs;
            rhs.reset();
        }
        else if (*this)
        {
            rhs = (*this);
            base_t::reset();
        }
        return (*this);
    }
};

/*
    Special swap algorithm
*/

template <typename T, typename U>
void swap(valid<T>& x, valid<U>& y)
{
    x.swap(y);
}

//////////////////////////////////////////////////////////////////////////
NX_END
//////////////////////////////////////////////////////////////////////////
